#include "LCD_Geometry.h"


static void NomalLine(struct LCD_Line * LineCord);

static void VerticalLine(struct LCD_Line * LineCord);

static void HorizontalLine(struct LCD_Line * LineCord);

static void NormalLine_Pos(UIN16 * Start_X, UIN16 * End_X, UIN16 * Start_Y, UIN16 * End_Y);

static void NormalLine_Neg(UIN16 * Start_X, UIN16 * End_X, UIN16 * Start_Y, UIN16 * End_Y);

inline static DOUBLE GetDx(UIN16 * Start_X, UIN16 * End_X, UIN16 * Start_Y, UIN16 * End_Y);

/**
 * @brief Global, This function is called to draw a straight line on the screen
 *
 * @param struct LCD_Line * LineCord
 *
 * @reval None
 */

void Geo_Line(struct LCD_Line * LineCord)
{
	if(((LineCord->Start_X) != (LineCord->End_X)) && ((LineCord->Start_Y) != (LineCord->End_Y)))
	{
		NomalLine(LineCord);
	}

	if(((LineCord->Start_X) == (LineCord->End_X)) && ((LineCord->Start_Y) != (LineCord->End_Y)))
	{
		VerticalLine(LineCord);
	}

	if(((LineCord->Start_Y) == (LineCord->End_Y)) && ((LineCord->Start_X) != (LineCord->End_X)))
	{
		HorizontalLine(LineCord);
	}

	if(((LineCord->Start_Y) == (LineCord->End_Y)) && ((LineCord->Start_X) != (LineCord->End_X)))
	{
		HorizontalLine(LineCord);
	}

	return;
}

/**
 * @brief Draw the vertical line, which has X-start equals to X-end
 *
 * @param struct LCD_Line * LineCord
 *
 * @reval None
 */
static void VerticalLine(struct LCD_Line * LineCord)
{
	UIN16 Y_Start = LineCord->Start_Y;
	UIN16 Y_End	  = LineCord->End_Y;

	struct LCD_Coord Temp_Win_Line;

	if( Y_Start > Y_End)
	{
		Swap16(&Y_Start, &Y_End);
	}
	else
	{
		/* Do nothing as Y_Start is already smaller than Y_End */
	}

	Temp_Win_Line.X_Add = LineCord->Start_X;
	Temp_Win_Line.Y_Add = LineCord->Start_Y;
	Temp_Win_Line.Width = ONE_16;
	Temp_Win_Line.Height= (LineCord->End_Y) - (LineCord->Start_Y) + ONE_16;

	LCD_Clear(BLACK,&Temp_Win_Line);

	return;
}
/**
 * @brief Draw the horizontal line, which has Y-end equals to Y-Start
 *
 * @param struct LCD_Line * LineCord
 *
 * @reval None
 */
static void HorizontalLine(struct LCD_Line * LineCord)
{
		UIN16 X_Start = LineCord->Start_X;
		UIN16 X_End	  = LineCord->End_X;

		struct LCD_Coord Temp_Win_Line;

		if( X_Start > X_End)
		{
			Swap16(&X_Start, &X_End);
		}
		else
		{
			/* Do nothing as Y_Start is already smaller than Y_End */
		}

		Temp_Win_Line.X_Add = LineCord->Start_X;
		Temp_Win_Line.Y_Add = LineCord->Start_Y;
		Temp_Win_Line.Height = ONE_16;
		Temp_Win_Line.Width= (LineCord->End_X) - (LineCord->Start_X) + ONE_16;

		LCD_Clear(BLACK,&Temp_Win_Line);

	return;
}
/**
 * @brief This function draw the straight line as long as the begin X,Y are different from
 * 			End X, Y
 *
 * @param struct LCD_Line * LineCord
 *
 * @reval None
 *
 */
static void NomalLine(struct LCD_Line * LineCord)
{
	UIN16 X_Start = LineCord->Start_X;
	UIN16 X_End	  = LineCord->End_X;
	UIN16 Y_Start = LineCord->Start_Y;
	UIN16 Y_End	  = LineCord->End_Y;

	struct LCD_Coord Temp_Win_Line;

	if(LINE_CASE_01(X_Start, X_End, Y_Start, Y_End))
	{
		Swap16(&X_Start, &X_End);
		Swap16(&Y_Start, &Y_End);

		Temp_Win_Line.X_Add  = X_Start;
		Temp_Win_Line.Y_Add  = Y_Start;
		Temp_Win_Line.Width  = X_End - X_Start;
		Temp_Win_Line.Height = Y_End - Y_Start;

		LCD_SetWindow(&Temp_Win_Line);

		NormalLine_Pos(&X_Start, &X_End, &Y_Start, &Y_End);

		LCD_CloseWindow();
	}


	else if (LINE_CASE_02(X_Start, X_End, Y_Start, Y_End))
	{
		NormalLine_Neg(&X_Start, &X_End, &Y_Start, &Y_End);
	}

	else if (LINE_CASE_03(X_Start, X_End, Y_Start, Y_End))
	{
		Swap16(&X_Start, &X_End);
		Swap16(&Y_Start, &Y_End);
		NormalLine_Neg(&X_Start, &X_End, &Y_Start, &Y_End);
	}

	else if (LINE_CASE_04(X_Start, X_End, Y_Start, Y_End))
	{
		Temp_Win_Line.X_Add  = X_Start;
		Temp_Win_Line.Y_Add  = Y_Start;
		Temp_Win_Line.Width  = X_End - X_Start;
		Temp_Win_Line.Height = Y_End - Y_Start;

		LCD_SetWindow(&Temp_Win_Line);

		NormalLine_Pos(&X_Start, &X_End, &Y_Start, &Y_End);

		LCD_CloseWindow();
	}
	return;
}




static void NormalLine_Pos(UIN16 * Start_X, UIN16 * End_X, UIN16 * Start_Y, UIN16 * End_Y)
{
	DOUBLE Dx = GetDx(Start_X, End_X, Start_Y, End_Y);
	UIN16  Local_X 	= ZERO_16;
	UIN16  Local_Y  = ZERO_16;
	UIN16  Global_X = ZERO_16;
	UIN16  Global_Y	= ZERO_16;
	UIN16  	Temp	= ZERO_16;
	UIN16  Old_Y = ZERO_16;

	while( Global_X < (* End_X))
	{
		Local_Y  = ((UIN16)Dx);
		Global_X = *Start_X + Local_X;


		LCD_WriteReg(X_Address, Global_X);
		for(Temp = ONE_16; Temp <= Local_Y; Temp++)
		{
			if(Global_Y >= ONE_16)
			{
				Old_Y = Global_Y-ONE_16;
			}

			LCD_WriteReg(Y_Address, Global_Y);
			*Write_Reg = 0x0022;
			LCD_WritePixel(BLACK);
			LCD_WriteReg(Y_Address, Old_Y);
			*Write_Reg = 0x0022;
			LCD_WritePixel(BLACK);
			Global_Y++;
		}


		Local_X++;
	}


	return;
}

static void NormalLine_Neg(UIN16 * Start_X, UIN16 * End_X, UIN16 * Start_Y, UIN16 * End_Y)
{
	UIN16 Dx = GetDx(Start_X, End_X, Start_Y, End_Y);
	UIN16 Local_X 	= ZERO_16;
	UIN16 Local_Y   = ZERO_16;
	UIN16 Global_X 	= * Start_X;
	UIN16 Global_Y	= * Start_Y;

	while( Global_X < (* End_X))
	{
		Local_Y  = ((UIN16)(ceil(Dx * ((DOUBLE)Local_X))));
		Global_X = Global_X + Local_X;
		Global_Y = Global_Y - Local_Y;

		LCD_WriteReg(X_Address, Global_X);
		LCD_WriteReg(Y_Address, Global_Y);
		*Write_Reg = 0x0022;
		LCD_WritePixel(BLACK);

		Local_X++;
	}

	return;
}


inline static DOUBLE GetDx(UIN16 * Start_X, UIN16 * End_X, UIN16 * Start_Y, UIN16 * End_Y)
{
	DOUBLE Dx = ZERO_D;

	Dx = (((DOUBLE)*End_Y) - ((DOUBLE)*Start_Y)) DEVIDE_BY (((DOUBLE)*End_X) - ((DOUBLE)*Start_X));

	return Dx;
}


